#include "TcpConnection.h"
#include "Channel.h"
#include "EventLoop.h"
#include "Socket.h"
#include "Logger.h"

#include <functional>
#include <errno.h>
#include <unistd.h>

static EventLoop* CheckLoopNotNull(EventLoop* loop)
{
    if(loop == nullptr)
    {
        LOG_FATAL("%s:%s:%d TcpConnection is nullptr!\n", __FILE__, __FUNCTION__, __LINE__);
    }
    return loop;
}

TcpConnection::TcpConnection(EventLoop* loop, const std::string& name, int sockfd, const InetAddress& localAddr, const InetAddress& peerAddr)
    : loop_(CheckLoopNotNull(loop))
    , name_(name)
    , state_(kConnecting)
    , reading_(true)
    , socket_(new Socket(sockfd))
    , channel_(new Channel(loop, sockfd))
    , localAddr_(localAddr)
    , peerAddr_(peerAddr)
    , highWaterMark_(64*1024*1024)  // 水位线 64M
{
    // 给channel_设置一系列回调函数，poller给channel通知感兴趣的事件发生了，channel会回调相应的操作函数
    // channel对各种事件都感兴趣，但是Acceptor只对读事件感兴趣，所以当时只注册了读事件回调
    channel_->setReadCallback(std::bind(&TcpConnection::handleRead, this, std::placeholders::_1));
    channel_->setWriteCallback(std::bind(&TcpConnection::handleWrite, this));
    channel_->setCloseCallback(std::bind(&TcpConnection::handleClose, this));
    channel_->setErrorCallback(std::bind(&TcpConnection::handleError, this));

    LOG_INFO("TcpConnection::ctor[%s] at fd=%d.\n", name_.c_str(), sockfd);
    socket_->setKeepAlive(true);  // 启动tcp的保活机制
}

TcpConnection::~TcpConnection()
{
    LOG_INFO("TcpConnection::dtor[%s] at fd=%d state=%d.\n", name_.c_str(), channel_->fd(), (int)state_);
}

// 发送数据
void TcpConnection::send(const std::string& buf)
{
    if(state_ == kConnected)  // 连接建立状态
    {
        if(loop_->isInLoopThread())
        {
            // 当前线程是loop_所在线程，直接调用sendInLoop方法发送数据
            // 一般都是自己线程
            // 也有个别场景把TcpConnection记录下来，别的线程里send
            sendInLoop(buf.c_str(), buf.size());
        }
        else
        {
            // 如果当前线程不是loop_所在线程，使用loop_->runInLoop方法将发送操作调度到事件循环线程中执行
            loop_->runInLoop(std::bind(
                &TcpConnection::sendInLoop,
                this,
                buf.c_str(),
                buf.size()
            ));
        }
    }
}

// 关闭连接
void TcpConnection::shutdown()  // shutdown()是用户调用的，可能底层还有没发完的数据
{
    if(state_ == kConnected)
    {
        setState(kDisconnecting);
        loop_->runInLoop(std::bind(&TcpConnection::shutdownInLoop, this));
    }
}

// 连接建立，当TcpServer接受新连接时调用，每个连接只应调用一次
void TcpConnection::connectEstablished()
{
    setState(kConnected);  // 修改连接状态，构造函数中初始化为kConnecting
    channel_->tie(shared_from_this());  // 让channel_记录一下TcpConnection的存货状态
    channel_->enableReading();  // 向poller注册channal的epollin事件

    // 新连接建立，执行回调（用户预置的）
    connectionCallback_(shared_from_this());
}

// 连接销毁，当TcpServer将此TcpConnection从映射map中删除时调用，TcpServer::removeConnectionInLoop或者TcpServer析构中会调用
void TcpConnection::connectDestroyed()
{
    if(state_ == kConnected)
    {
        /**
         * 如果是 TcpConnection::handleClose() 逐步回调到这里，是不会进入这个代码块的
         * 因为 TcpConnection::handleClose() 中已经把 state_ 改成 kDisconnected了
         * 连接状态直接调用 TcpConnection::connectDestroyed() 才进来
         */
        setState(kDisconnected);
        channel_->disableAll();  // 把channel所有感兴趣的事件，从poller中delete
        connectionCallback_(shared_from_this());
    }
    channel_->remove();  // 把channel从poller中删除
}

void TcpConnection::handleRead(Timestamp receiveTime)
{
    int savedErrno = 0;
    ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
    if(n > 0)  // 正常读取
    {
        // 已建立连接的用户，有可读事件发生了，调用用户传入的onMessage回调操作
        // using MessageCallback = std::function<void(const TcpConnectionPtr& conn, Buffer* buf, Timestamp receiveTime)>;  // 有读写消息时的回调
        messageCallback_(shared_from_this()/* shared_from_this()获取当前对象的智能指针*/, &inputBuffer_, receiveTime);
    }
    else if(n == 0)  // 客户端断开了，关闭连接
    {
        handleClose();
    }
    else  // 出错了
    {
        errno = savedErrno;
        LOG_ERROR("TcpConnetion::handleRead error.\n");
        handleError();
    }
}

void TcpConnection::handleWrite()
{
    if(channel_->isWriting())
    {
        int savedErrno = 0;
        ssize_t n = outputBuffer_.writeFd(channel_->fd(), &savedErrno);
        if(n > 0)  // 有数据发送成功
        {
            outputBuffer_.retrieve(n);
            if(outputBuffer_.readableBytes() == 0)  // 发送完成了
            {
                channel_->disableWriting();  // 从可写变成不可写，要写的数据写完了
                if(writeCompleteCallback_)  // 数据写完的回调
                {
                    // 唤醒loop_对应的thread线程，执行回调
                    loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
                }
                if(state_ == kDisconnecting)  // 正在关闭
                {
                    // 发送缓冲区还有数据没发完，上面就打算关闭TcpConnection，shutdownInLoop()里什么都没做，等要发的发完再调用shutdownInLoop()
                    shutdownInLoop();  // 在当前loop中删掉这条TcpConnection
                }
            }
        }
        else  // n <= 0，=0是连接关闭了
        {
            LOG_ERROR("TcpConnection::handleWrite .\n");
        }
    }
    else  // channel不可写
    {
        LOG_ERROR("TcpConnection fd=%d is down, no more writing.\n", channel_->fd());
    }
}

// poller通知channel::closeCallback_ → TcpConnection::handleClose()
void TcpConnection::handleClose()
{
    LOG_INFO("TcpConnection::handleClose() fd=%d state=%d.\n", channel_->fd(), (int)state_);
    // assert(state_ == kConnected || state_ == kDisconnecting)  已连接或者正在断开的connection才能close
    setState(kDisconnected);
    channel_->disableAll();

    TcpConnectionPtr connPtr(shared_from_this());
    if(connectionCallback_)
    {
        connectionCallback_(connPtr);  // 执行连接建立或关闭的回调，用户设置的
    }
    if(closeCallback_)
    {
        closeCallback_(connPtr);  // 关闭连接的回调，TcpServer给的TcpServer::newConnection里conn->setCloseCallback绑定的TcpServer::removeConnection
    }
}

void TcpConnection::handleError()
{
    int optval;  // 存储从套接字选项中获取的错误码
    socklen_t optlen = static_cast<socklen_t>(sizeof optval);  // optlen，用来指定optval的大小，这里使用了类型转换确保optlen的类型正确
    int err = 0;  // 存储最终错误码

    if(::getsockopt(channel_->fd(), SOL_SOCKET, SO_ERROR, &optval, &optlen) < 0)  // 调用getsockopt系统调用来获取指定套接字的SO_ERROR选项，即最后一个操作中可能发生的错误
    {
        // 返回值 < 0，出错了，说明在尝试获取错误码的过程中发生了新的错误，此时将errno的值赋给err
        err = errno;
    }
    else  // getsockopt调用成功，则将optval的值赋给err
    {
        // optval现在包含了最后一次操作的错误码，如果没有发生错误，则optval应该是0
        err = optval;
    }
    LOG_ERROR("TcpConnection::handleError() name:%s - SO_ERROR:%d", name_.c_str(), err);
}

// 发送数据 应用写得快，而内核发送数据慢，需要把待发送的数据写入缓冲区，且设置了水位回调，防止发送写太快
/**
 * @brief 
 * 实际发送数据
 * @param data 要发送的数据的指针
 * @param len 要发送数据的长度
 */
void TcpConnection::sendInLoop(const void* data, size_t len)
{
    ssize_t nwrote = 0;  // 记录已经发送的长度
    size_t remaining = len;  // 还没发送的数据
    bool faultError = false;  // 是否产生错误

    if(state_ == kDisconnected)  // 之前调用过该连接的shutdown，连接都关了，不能再发送了
    {
        LOG_ERROR("disconnected, give up writing!\n");
        return ;
    }

    // channel_第一次开始写数据，而且缓冲区没有待发送数据
    if(!channel_->isWriting() && outputBuffer_.readableBytes() == 0)
    {
        nwrote = ::write(channel_->fd(), data, len);  // 返回实际发送长度
        if(nwrote >= 0)  // 发送成功了
        {
            remaining = len - nwrote;
            if(remaining == 0 && writeCompleteCallback_)  // 所有数据已发送完毕，写入完成回调也注册了
            {
                // 既然一次性发送完成，就不用再给channel_设置epollout事件了
                loop_->queueInLoop(std::bind(writeCompleteCallback_, shared_from_this()));
            }
        }
        else  // nwrote < 0
        {
            nwrote = 0;
            if(errno != EWOULDBLOCK)  // EWOULDBLOCK -- 非阻塞操作没有数据可发送，这是正常情况
            {
                LOG_ERROR("TcpConnection::sendInLoop error.\n");
                if(errno == EPIPE || errno == ECONNRESET)  // SIGPIPI  RESET
                {
                    faultError = true;
                }
            }
        }
    }

    // 处理未发送完的数据
    // 当前这一次write并没有把数据全部发送数据，剩余的数据需要保存到缓冲区，然后给Channel注册epollout事件
    // poller发现tcp的发送缓冲区有空间，会通知相应的sock-channel，调用channel的wrteCallback_方法
    // channel的wrteCallback_还是TcpConnection给的，给的就是TcpConnection::handleWrite()
    // 也就是调用TcpConnection::handleWrite()方法，把发送缓冲区中的数据全部发送完成
    if(!faultError && remaining >0)  // 如果没有发生错误且还有未发送的数据
    {
        size_t oldLen = outputBuffer_.readableBytes();  // 目前发送缓冲区剩余的待发送数据的长度
        if(oldLen + remaining >= highWaterMark_
            && oldLen < highWaterMark_  // 如果oldLen > highWaterMark_，上一次已经调过高水位回调了
            && highWaterMarkCallback_)
        {
            loop_->queueInLoop(std::bind(highWaterMarkCallback_, shared_from_this(), oldLen + remaining));
        }
        outputBuffer_.append(static_cast<const char*>(data)+nwrote, remaining);  // 将未发送的数据追加到输出缓冲区
        if(!channel_->isWriting())
        {
            channel_->enableWriting();  // 这里一定要注册channel的写事件，不然poller不会给channel通知epollout
        }
    }
}

void TcpConnection::shutdownInLoop()
{
    if(!channel_->isWriting())  // 没有注册写事件，当前outputBuffer发送缓冲区要发送的数据都发完了
    {
        socket_->shutdownWrite();  // 关闭写端，会触发EPOLLHUP事件（这个不用专门向epoll注册）
    }
}